﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Datos;

namespace Negocio
{
    public abstract class Base
    {
        #region Atributos Privados

        /// <summary>
        /// Mantiene las relaciones con otros objetos, a través de las claves foráneas.
        /// </summary>
        protected Dictionary<string, object> relaciones = new Dictionary<string, object>();

        #endregion

        /// <summary>
        /// Guarda referencias de claves foráneas.
        /// </summary>
        /// <param name="nombre">Nombre del campo FK en tabla.</param>
        /// <param name="valor">Valor de FK en tabla.</param>
        protected void SetClave(string nombre, object valor)
        {
            if (valor == DBNull.Value) valor = null;

            if (relaciones.ContainsKey(nombre))
                relaciones[nombre] = valor;
            else
                relaciones.Add(nombre, valor);
        }

        /// <summary>
        /// Devuelve el valor de clave foránea si es que existe.
        /// </summary>
        /// <param name="nombre">Nombre del campo FK en tabla.</param>
        protected object GetClave(string nombre)
        {
            if (!relaciones.ContainsKey(nombre)) throw new ErrorException("Valor invalido null");
            return relaciones[nombre];
        }

        /// <summary>
        /// Devuelve el valor de clave foránea si es que existe.
        /// </summary>
        /// <param name="nombre">Nombre del campo FK en tabla.</param>
        protected object GetClaveONull(string nombre)
        {
            if (!relaciones.ContainsKey(nombre)) return null;

            return relaciones[nombre];
        }

        #region Estado

        protected EstadoDeObjeto estadoDeObjeto = EstadoDeObjeto.Nuevo;

        /// <summary>
        /// Indica si el Objeto no ha sido persistido nunca aun.
        /// </summary>
        public bool Nuevo
        {
            get { return estadoDeObjeto == EstadoDeObjeto.Nuevo; }
        }

        /// <summary>
        /// Indica si alguna de sus propiedades ha cambiado.
        /// </summary>
        public virtual bool Modificado
        {
            get { return estadoDeObjeto == EstadoDeObjeto.ViejoModificado; }
        }

        /// <summary>
        /// Endica si el Objeto fue marcado para ser borrado.
        /// </summary>
        public bool Borrado
        {
            get
            {
                return estadoDeObjeto == EstadoDeObjeto.BorradoNuevo ||
                       estadoDeObjeto == EstadoDeObjeto.BorradoViejo;
            }
        }

        /// <summary>
        /// Marca el Objeto como Nuevo.
        /// </summary>
        protected void MarcarNuevo()
        {
            estadoDeObjeto = EstadoDeObjeto.Nuevo;
        }

        /// <summary>
        /// Marca el Objeto como Viejo (ha sido persistido su estado en algn momento).
        /// </summary>
        protected void MarcarViejo()
        {
            estadoDeObjeto = EstadoDeObjeto.ViejoLimpio;
        }

        /// <summary>
        /// Marca el Objeto para ser borrado.
        /// </summary>
        protected void MarcarBorrado()
        {
            estadoDeObjeto = estadoDeObjeto == EstadoDeObjeto.Nuevo ? EstadoDeObjeto.BorradoNuevo : EstadoDeObjeto.BorradoViejo;
        }

        /// <summary>
        /// Marca el Objeto como Modificado cuando el valor de alguna propiedad ha cambiado.
        /// </summary>
        public virtual void MarcarModificado()
        {
            if (!this.Nuevo && !this.Borrado)
                estadoDeObjeto = EstadoDeObjeto.ViejoModificado;

            if (ModificadoEvent != null) { ModificadoEvent(); }
        }

        /// <summary>
        /// Marca el Objeto como Limpio (sus propiedades no han cambiado).
        /// </summary>
        protected void MarcarLimpio()
        {
            if (!this.Nuevo && !this.Borrado)
                estadoDeObjeto = EstadoDeObjeto.ViejoLimpio;
        }

        /// <summary>
        /// Indica que el Objeto debe ser borrado.
        /// </summary>
        public void Borrar()
        {
            estadoDeObjeto = estadoDeObjeto == EstadoDeObjeto.Nuevo ? EstadoDeObjeto.BorradoNuevo : EstadoDeObjeto.BorradoViejo;
        }

        #endregion

        #region Mecanismo de Persistencia

        /// <summary>
        /// Hace persistentes los cambios del Objeto en la Base de Datos
        /// </summary>
        public void Persistir()
        {
            EstadoDeObjeto estadoOriginal = this.estadoDeObjeto;
            try
            {
                if (!Validado)
                {
                    throw new InconsistenciaException("Datos invalidos");
                }
                if (this.Borrado)
                {
                    if (this.estadoDeObjeto == EstadoDeObjeto.BorradoViejo)
                    {
                        DeleteDB();
                    }
                }
                else
                {
                    // Verificar si es para Crear o Modificar
                    if (this.Nuevo)
                    {
                        InsertDB();
                    }
                    else if (this.Modificado) //Si no es Nuevo, verificar si ha sido modificado
                    {
                        UpdateDB();
                    }
                }
                if (this.Borrado)
                    // Objeto borrado ahora es Nuevo
                    MarcarNuevo();
                else
                    MarcarViejo();
            }
            catch (Exception ex)
            {
                //Restaurar el estado anterior al intento de persistir
                this.estadoDeObjeto = estadoOriginal;
                throw ex;
            }
        }

        /// <summary>
        /// Propaga la Persistencia a los objetos hijos.
        /// </summary>
        protected virtual void PersistirHijos()
        {
            throw new System.NotImplementedException();
        }
        #endregion

        #region Acceso a Base de Datos
        protected virtual void InsertDB()
        {
            throw new System.NotImplementedException();
        }

        protected virtual void UpdateDB()
        {
            throw new System.NotImplementedException();
        }

        protected virtual void DeleteDB()
        {
            throw new System.NotImplementedException();
        }

        protected virtual bool LeerDB()
        {
            throw new System.NotImplementedException();
        }

        protected virtual void CargarDR(IDataReader dr)
        {
            throw new System.NotImplementedException();
        }

        #endregion
        
        #region Reglas de Negocio

        /// <summary>
        /// Devuelve <i>true</i> si el Objeto posee un estado válido. O sea que no quiebra ninguna Regla de Negocio.
        /// Devuelve <i>false</i> si el Objeto posee alguna Regla de Negocio Quebrada.
        /// </summary>
        public virtual bool Validado
        {
            get
            {
                Validar();
                //TODO: Ver como solucione este
                return true;
                //return reglasQuebradas.Count == 0;
            }
        }

        /// <summary>
        /// Permite realizar validaciones extras antes de Persistir el estado del objeto.
        /// </summary>
        public virtual void Validar()
        {
            throw new System.NotImplementedException();
        }

        #endregion

        #region Evento para informar que se modificó la Lista

        public event ModificadoHandler ModificadoEvent;
        public delegate void ModificadoHandler();

        #endregion

        #region enum Estado De Persistencia

        public enum EstadoDeObjeto
        {
            Nuevo,
            ViejoLimpio,
            ViejoModificado,
            BorradoNuevo,
            BorradoViejo
        }

        #endregion
    }
}
